package com.net.xpay.admin.secutiry;

import com.google.common.base.Strings;
import com.net.common.exception.BusinessException;
import com.net.common.util.StreamUtil;
import com.net.xpay.common.domain.CheckCode;
import com.net.xpay.common.domain.admin.rbac.AdminUser;
import com.net.xpay.common.domain.admin.rbac.Permission;
import com.net.xpay.common.enums.user.CheckCodeType;
import com.net.xpay.common.manager.CheckCodeManager;
import com.net.xpay.common.manager.admin.rbac.AdminUserManager;
import com.net.xpay.common.service.admin.rbac.PermissionService;
import com.net.xpay.common.validator.CheckCodeValidator;
import com.net.xpay.core.constant.PoseidonErrorCode;
import org.mindrot.jbcrypt.BCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;

/**
  on 01/02/2018.
 */
@Component
public class FormLoginAuthenticationProvider implements AuthenticationProvider {
    private static final Logger LOGGER = LoggerFactory.getLogger(FormLoginAuthenticationProvider.class);

    @Autowired
    private CheckCodeValidator checkCodeValidator;
    @Autowired
    private CheckCodeManager checkCodeManager;
    @Autowired
    private AdminUserManager adminUserManager;
    @Autowired
    private PermissionService permissionService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        FormLoginToken token = (FormLoginToken) authentication;
        if (Strings.isNullOrEmpty(token.getMobile())) {
            throw new WebAuthenticationException(PoseidonErrorCode.MOBILE_ERROR, "手机号格式不正确");
        }

        try {
            return doAuthentication(token);
        } catch (AuthenticationException e) {
            throw e;
        } catch (Exception e) {
            LOGGER.warn("adminUser authenticated failed|mobile={}|password={}|checkCode={}",
                    token.getMobile(), token.getPassword(), token.getCheckCode(), e);
            throw new WebAuthenticationException(PoseidonErrorCode.BUSINESS_ERROR, "登陆服务暂时不可用,请稍后重试");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return FormLoginToken.class.isAssignableFrom(authentication);
    }

    private Authentication doAuthentication(FormLoginToken token) throws AuthenticationException {
        AdminUser adminUser = adminUserManager.findByMobile(token.getMobile());
        if (adminUser == null) {
            throw new WebAuthenticationException(PoseidonErrorCode.MOBILE_NOT_FOUND, "手机号没找到");
        }

        verifyCredential(token, adminUser);

        //查询权限信息
        List<Permission> permissions = permissionService.findByAdminUserId(adminUser.getId());
        Collection<GrantedAuthority> authorities = StreamUtil.map(permissions, permission -> new SimpleGrantedAuthority(permission.getName()));
        return new UsernamePasswordAuthenticationToken(adminUser.getId(), null, authorities);
    }

    private void verifyCredential(FormLoginToken token, AdminUser user) throws AuthenticationException {
        switch (token.getLoginMethod()) {
            case PASSWORD_CHECK_CODE:
                verifyPasswordCheckCode(token,user);
                break;
            default:
                throw new WebAuthenticationException(PoseidonErrorCode.BUSINESS_ERROR, "登陆方式不正确,请稍后重试");
        }
    }



    private void verifyPasswordCheckCode(FormLoginToken token,AdminUser user) throws AuthenticationException {
        try {
            //验证密码
            String password = token.getPassword() == null ? "" : token.getPassword();
            if (!BCrypt.checkpw(password, user.getPassword())) {
                throw new WebAuthenticationException(PoseidonErrorCode.PASSWORD_ERROR, "密码不正确");
            }
            //验证验证码
            CheckCode checkCode = checkCodeManager.findByMobileAndType(token.getMobile(), CheckCodeType.ADMIN_LOGIN);

            checkCodeValidator.validateCheckCode(checkCode, token.getCheckCode());
        } catch (BusinessException e) {
            throw new WebAuthenticationException(PoseidonErrorCode.CHECK_CODE_ERROR, e.getErrorMessage());
        }
    }
}